/*
 * Copyright (c) 2023-2023 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.swagger.config;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.Scopes;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import java.util.ArrayList;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.elsfs.cloud.common.swagger.support.SwaggerProperties;
import org.springdoc.core.utils.SpringDocUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.http.HttpHeaders;

/**
 * swagger配置
 *
 * <p>禁用方法1：使用注解@Profile({"dev","test"})
 *
 * <p>表示在开发或测试环境开启，而在生产关闭。（推荐使用） 禁用方法2：使用注解@ConditionalOnProperty(name =
 * "swagger.enable",havingValue = "true")
 * 然后在测试配置或者开发配置中添加swagger.enable=true即可开启，生产环境不填则默认关闭Swagger.
 *
 * @author zeng
 */
@RequiredArgsConstructor
@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true)
public class OpenAPIDefinition extends OpenAPI
    implements InitializingBean, ApplicationContextAware {

  @Setter private String path;

  private ApplicationContext applicationContext;

  /**
   * api 模式
   *
   * @return securityScheme
   */
  private SecurityScheme apiKey() {
    SecurityScheme securityScheme = new SecurityScheme();
    // 类型
    securityScheme.setType(SecurityScheme.Type.APIKEY);
    // 请求头的name
    securityScheme.setName(HttpHeaders.AUTHORIZATION);
    // token所在未知
    securityScheme.setIn(SecurityScheme.In.HEADER);
    return securityScheme;
  }

  /**
   * oauth 模式
   *
   * @param swaggerProperties p
   * @return securityScheme
   */
  private SecurityScheme oauth2(SwaggerProperties swaggerProperties) {
    OAuthFlow clientCredential = new OAuthFlow();
    clientCredential.setTokenUrl(swaggerProperties.getTokenUrl());
    clientCredential.setScopes(
        new Scopes().addString(swaggerProperties.getScope(), swaggerProperties.getScope()));
    OAuthFlows oauthFlows = new OAuthFlows();
    oauthFlows.password(clientCredential);
    SecurityScheme securityScheme = new SecurityScheme();
    securityScheme.setType(SecurityScheme.Type.OAUTH2);
    securityScheme.setFlows(oauthFlows);
    return securityScheme;
  }

  private Info info(SwaggerProperties swaggerProperties) {
    return new Info()
        .title(swaggerProperties.getTitle())
        .version(swaggerProperties.getVersion())
        .description(swaggerProperties.getTitle())
        .termsOfService(swaggerProperties.getDescription());
  }

  @Override
  public void afterPropertiesSet() {
    SwaggerProperties swaggerProperties = applicationContext.getBean(SwaggerProperties.class);
    this.info(info(swaggerProperties));
    // oauth2.0
    this.schemaRequirement(HttpHeaders.AUTHORIZATION, this.oauth2(swaggerProperties));
    // oauth2.0 apikey
    //    this.schemaRequirement(HttpHeaders.AUTHORIZATION, this.apiKey());

    // 全局安全校验项，也可以在对应的controller上加注解SecurityRequirement
    this.addSecurityItem(new SecurityRequirement().addList(HttpHeaders.AUTHORIZATION));
    // servers
    List<Server> serverList = new ArrayList<>();
    serverList.add(new Server().url(swaggerProperties.getGateway() + "/" + path));
    this.servers(serverList);
    // 支持参数平铺
    SpringDocUtils.getConfig().addSimpleTypesForParameterObject(Class.class);
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
  }
}
